//	--------------------------------------------------------------------
//	Copyright(C) 2006,2007 Zhao Yukun. All rights reserved.
//	This file is a part of the Xeres system.
//	Xeres 2007
//	Contact : xeres.engine@gmail.com
//	$(reservedInfo)
//	$(reservedDeclaration)
//	--------------------------------------------------------------------
///	@file	<object_ref_base.h>
///	@path	~/src/object/
///	@date	2008/01/12
///	@desc	Base class & helpers of Ref/RefPass/RefWeak.

#pragma once

#include "xeres.h"

namespace xeres {

	// forward declaration
	class IObject;

#ifndef _DOC_GEN

	/*!
		\class	ObjectOwnershipHelper
		\brief	Helper function to maintain IObject ownership.
	*/
	class ObjectOwnershipHelper
	{
	public:

		// Borrow IObject ownership.
		template<
			typename _IObjectClass ,
			typename _OtherIObject
		> static inline void BorrowOwnership( _IObjectClass *& src , _OtherIObject *& dst )
		{
			TRACK_FUNCTION_SCOPE_MSG( _S("Compatible check %s:%s.") ,
				_IObjectClass::StaticClassName().c_str() ,
				_OtherIObject::StaticClassName().c_str() );

			assert( dst == NULL );
			if( src == NULL )
				return;
			// check compatible
			if( !src->IsA<_OtherIObject>() )
			{
				TRACE_ERROR( _S("ObjectOwnershipHelper::BorrowOwnership: Assigning in-compatible class objects: from %s to %s.") ,
					_IObjectClass::StaticClassName().c_str() ,
					_OtherIObject::StaticClassName().c_str() );
				FATAL_ABORT( _S("BorrowOwnership error") );
			}
			dst = static_cast<_OtherIObject*>(src);
		}

		// Duplicate IObject ownership.
		template<
			typename _IObjectClass ,
			typename _OtherIObject
		> static inline void DuplicateOwnership( _IObjectClass *& src , _OtherIObject *& dst )
		{
			TRACK_FUNCTION_SCOPE_MSG( _S("Compatible check %s:%s.") ,
				_IObjectClass::StaticClassName().c_str() ,
				_OtherIObject::StaticClassName().c_str() );

			assert( dst == NULL );
			if( src == NULL )
				return;
			// check compatible
			if( !src->IsA<_OtherIObject>() )
			{
				TRACE_ERROR( _S("ObjectOwnershipHelper::DuplicateOwnership: Assigning in-compatible class objects: from %s to %s.") ,
					_IObjectClass::StaticClassName().c_str() ,
					_OtherIObject::StaticClassName().c_str() );
				FATAL_ABORT( _S("DuplicateOwnership error") );
			}
			src->AddRef();
			dst = static_cast<_OtherIObject*>(src);
		}

		// Transfer IObject ownership.
		template<
			typename _IObjectClass ,
			typename _OtherIObject
		> static inline void TransferOwnership( _IObjectClass *& src , _OtherIObject *& dst )
		{
			TRACK_FUNCTION_SCOPE_MSG( _S("Compatible check %s:%s.") ,
				_IObjectClass::StaticClassName().c_str() ,
				_OtherIObject::StaticClassName().c_str() );

			assert( dst == NULL );
			if( src == NULL )
				return;
			// check compatible
			if( !src->IsA<_OtherIObject>() )
			{
				TRACE_ERROR( _S("ObjectOwnershipHelper::TransferOwnership: Assigning in-compatible class objects: from %s to %s.") ,
					_IObjectClass::StaticClassName().c_str() ,
					_OtherIObject::StaticClassName().c_str() );
				FATAL_ABORT( _S("TransferOwnership error") );
			}
			dst = static_cast<_OtherIObject*>(src);
			src = NULL;
		}

		// Swap IObject ownership.
		template<
			typename _IObjectClass
		> static inline void SwapOwnership( _IObjectClass *& src , _IObjectClass *& dst )
		{
			_IObjectClass * mid = dst;
			dst = src;
			src = mid;
		}
	};

	// forward declaration
	template< typename _IObjectClass > class Ref;
	template< typename _IObjectClass > class RefPass;
	template< typename _IObjectClass > class RefWeak;

	/*!
		\class	RefBase
		\brief	Reference base.
	*/
	template<
		typename _IObjectClass , // IObject interface or class
		typename _RefClass // reference implementation
	> class RefBase : public ObjectOwnershipHelper
	{
	public:

		/*!
			\brief		Determine if object is null.
		*/
		bool IsNull( void ) const
		{
			return static_cast<const _RefClass*>(this)->m_object == NULL;
		}

		/*!
			\brief		Determine if object is valid.
		*/
		bool IsValid( void ) const
		{
			return static_cast<const _RefClass*>(this)->m_object != NULL;
		}

		/*!
			\brief		Get object pointer.
		*/
		_IObjectClass * operator -> ( void )
		{
#ifdef _DEBUG
			if( !IsValid() )
			{
				FATAL_ABORT( _S("Using null IObject instance detected.") );
			}
#else
			assert( IsValid() );
#endif /*_DEBUG*/
			return static_cast<_RefClass*>(this)->m_object;
		}

		/*!
			\brief		Get object pointer(constant).
		*/
		const _IObjectClass * operator -> ( void ) const
		{
#ifdef _DEBUG
			if( !IsValid() )
			{
				FATAL_ABORT( _S("Using null IObject instance detected.") );
			}
#else
			assert( IsValid() );
#endif /*_DEBUG*/
			return static_cast<const _RefClass*>(this)->m_object;
		}

		/*!
			\brief		Get reference.
		*/
		_IObjectClass& operator * ( void )
		{
#ifdef _DEBUG
			if( !IsValid() )
			{
				FATAL_ABORT( _S("Using null IObject instance detected.") );
			}
#else
			assert( IsValid() );
#endif /*_DEBUG*/
			return *( static_cast<const _RefClass*>(this)->m_object );
		}

		/*!
			\brief		Get reference(constant).
		*/
		const _IObjectClass& operator * ( void ) const
		{
#ifdef _DEBUG
			if( !IsValid() )
			{
				FATAL_ABORT( _S("Using null IObject instance detected.") );
			}
#else
			assert( IsValid() );
#endif /*_DEBUG*/
			return *( static_cast<_RefClass*>(this)->m_object );
		}

// 		/*!
// 			\brief		Disable addressing operator.
// 		*/
// 		_IObjectClass ** operator & ( void )
// 		{
// 			return &( static_cast<_RefClass*>(this)->m_object );
// 		}
// 
// 		/*!
// 			\brief		Disable addressing operator.
// 		*/
// 		_IObjectClass * const * operator & ( void ) const
// 		{
// 			return &( static_cast<const _RefClass*>(this)->m_object );
// 		}

		/*!
			\brief		Comparison operator.
		*/
		template<
			typename _OtherIObject
		> bool operator == ( const _OtherIObject * rhs ) const
		{
			return ( static_cast<const _RefClass*>(this)->m_object ) == rhs;
		}

		/*!
			\brief		Comparison operator.
		*/
		template<
			typename _OtherIObject
		> bool operator != ( const _OtherIObject * rhs ) const
		{
			return ( static_cast<const _RefClass*>(this)->m_object ) != rhs;
		}

		/*!
			\brief		Compare other object.
		*/
		bool IsTheSame( const void * rhs ) const
		{
			return ( static_cast<const _RefClass*>(this)->m_object ) == rhs;
		}

		/*!
			\brief		Compare other object.
		*/
		bool IsDifferent( const void * rhs ) const
		{
			return ( static_cast<const _RefClass*>(this)->m_object ) != rhs;
		}

		/*!
			\brief		Compare other object.
		*/
		bool Less( const void * rhs ) const
		{
			return ( static_cast<const _RefClass*>(this)->m_object ) < rhs;
		}

		/*!
			\brief		Compare other object.
		*/
		bool Greater( const void * rhs ) const
		{
			return ( static_cast<_RefClass*>(this)->m_object ) > rhs;
		}

		// comparison
		template< typename _OtherIObject > bool operator == ( const Ref< _OtherIObject >& rhs ) const
		{
			return rhs.IsTheSame( static_cast<const _RefClass*>(this)->m_object );
		}

		template< typename _OtherIObject > bool operator != ( const Ref< _OtherIObject >& rhs ) const
		{
			return rhs.IsDifferent( static_cast<const _RefClass*>(this)->m_object );
		}

		template< typename _OtherIObject > bool operator == ( const RefPass< _OtherIObject >& rhs ) const
		{
			return rhs.IsTheSame( static_cast<const _RefClass*>(this)->m_object );
		}

		template< typename _OtherIObject > bool operator != ( const RefPass< _OtherIObject >& rhs ) const
		{
			return rhs.IsDifferent( static_cast<const _RefClass*>(this)->m_object );
		}

		template< typename _OtherIObject > bool operator == ( const RefWeak< _OtherIObject >& rhs ) const
		{
			return rhs.IsTheSame( static_cast<const _RefClass*>(this)->m_object );
		}

		template< typename _OtherIObject > bool operator != ( const RefWeak< _OtherIObject >& rhs ) const
		{
			return rhs.IsDifferent( static_cast<const _RefClass*>(this)->m_object );
		}

		// less
		template< typename _OtherIObject > bool operator < ( const Ref< _OtherIObject >& rhs ) const
		{
			return rhs.Less( static_cast<const _RefClass*>(this)->m_object );
		}

		template< typename _OtherIObject > bool operator > ( const Ref< _OtherIObject >& rhs ) const
		{
			return rhs.Greater( static_cast<const _RefClass*>(this)->m_object );
		}

		template< typename _OtherIObject > bool operator < ( const RefPass< _OtherIObject >& rhs ) const
		{
			return rhs.Less( static_cast<const _RefClass*>(this)->m_object );
		}

		template< typename _OtherIObject > bool operator > ( const RefPass< _OtherIObject >& rhs ) const
		{
			return rhs.Greater( static_cast<const _RefClass*>(this)->m_object );
		}

		template< typename _OtherIObject > bool operator < ( const RefWeak< _OtherIObject >& rhs ) const
		{
			return rhs.Less( static_cast<const _RefClass*>(this)->m_object );
		}

		template< typename _OtherIObject > bool operator > ( const RefWeak< _OtherIObject >& rhs ) const
		{
			return rhs.Greater( static_cast<const _RefClass*>(this)->m_object );
		}
	};

#endif /*_DOC_GEN*/

} // namespace xeres
